package org.neptune.common.util;

import java.io.*;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

/** 序列化工具 */
public class SerializerUtil {

	private static final Logger logger = LoggerFactory.getLogger(SerializerUtil.class);

	/** schema缓存 */
	private static Map<Class<?>, Schema<?>> schemaMap = Maps.newConcurrentMap();

	/**
	 * 获取schema
	 *
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> Schema<T> getSchema(Class<T> clazz) {

		if (schemaMap.containsKey(clazz)){
			return (Schema<T>) schemaMap.get(clazz);
		}
		Schema<T> schema = RuntimeSchema.getSchema(clazz);
		schemaMap.put(clazz, schema);
		return schema;
	}

	/**
	 * 对象序列化
	 *
	 * @param obj
	 * @param <T>
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> byte[] objToByte(T obj) {
		if (obj == null){
			return null;
		}
		Class<T> clazz = (Class<T>) obj.getClass();
		LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		try{
			byte[] cacheByte = ProtostuffIOUtil.toByteArray(obj, getSchema(clazz), linkedBuffer);
			return cacheByte;
		} catch (Exception e){
			logger.error("serialize obj error : ", e);
		} finally{
			linkedBuffer.clear();
		}
		return null;
	}

	/**
	 * 反序列化
	 *
	 * @param data
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	public static <T> T byteToObj(byte[] data, Class<T> clazz) {
		try{
			T obj = clazz.newInstance();
			Schema<T> schema = getSchema(clazz);
			ProtostuffIOUtil.mergeFrom(data, obj, schema);
			return obj;
		} catch (Exception e){
			logger.error("serialize to obj error : ", e);
		}

		return null;
	}

	/**
	 * 把对象转化为byte数组 --JDK方式 解决protostuff默认无法序列化transient字段
	 * 
	 * @param obj
	 * @return
	 */
	public static <T> byte[] objToByteJDK(T obj) {
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		byte[] bytes = null;
		try{
			ObjectOutputStream oo = new ObjectOutputStream(bo);
			oo.writeObject(obj);
			bytes = bo.toByteArray();
		} catch (IOException e){
			logger.error("serialize to obj error : ", e);
		}
		return bytes;
	}

	/**
	 * 把byte数组还原为session
	 * 
	 * @param bytes
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T byteToObjJDK(byte[] bytes, Class<T> clazz) {
		ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
		ObjectInputStream in;
		try{
			T obj = clazz.newInstance();
			in = new ObjectInputStream(bi);
			obj = (T) in.readObject();
			return obj;
		} catch (Exception e){
			logger.error("serialize to obj error : ", e);
		}
		return null;
	}
}
